package values

import (
	"encoding/json"
	"errors"
	"fmt"
	"strconv"
	"strings"
	"time"

	"github.com/spf13/cast"
)

// ToTimeUTC 转换目标为time.Time(UTC时区), 转换失败则返回空Time对象
//   - 如待转换的any目标是日期时间字符串未包含时区信息时则使用UTC时区
func ToTime(v any) time.Time {
	if v == nil {
		return time.Time{}
	}
	to, _ := ToTimeWith(v, time.UTC)
	return to
}

// ToTimeE  转换目标为time.Time(UTC时区), 转换失败则返回空Time对象和错误信息
//   - 如待转换的any目标是日期时间字符串未包含时区信息时则使用UTC时区
func ToTimeE(v any) (time.Time, error) {
	if v == nil {
		return time.Time{}, errors.New("ToTimeE: nil pointer not support")
	}
	switch vs := v.(type) {
	case time.Time:
		return vs, nil
	}
	if to, err := ToTimeWith(v, time.UTC); err == nil {
		return to, nil
	}
	return time.Time{}, fmt.Errorf("ToTimeE: unable to %#v of type %T to time.Time", v, v)
}

// ToTimeD  转换目标为time.Time(UTC时区), 转换失败则返回defaultVal指定的Time对象
//   - 如待转换的any目标是日期时间字符串未包含时区信息时则使用UTC时区
func ToTimeD(v any, defaultVal time.Time) time.Time {
	if v == nil {
		return defaultVal
	}
	if to, err := ToTimeWith(v, time.UTC); err != nil {
		return defaultVal
	} else {
		return to
	}
}

// ToTimeWith 转换目标为指定的时区的time.Time(指定时区), 转换失败则返回空Time对象和错误信息
//   - 如待转换的any目标是日期时间字符串未包含时区信息时则使用location指定的时区
func ToTimeWith(v any, location *time.Location) (time.Time, error) {
	if v == nil {
		return time.Time{}, errors.New("ToTimeWith: nil pointer not support")
	}
	if location == nil {
		return time.Time{}, errors.New("ToTimeWith: location is nil")
	}
	switch vs := v.(type) {
	case time.Time:
		if vs.IsZero() {
			return vs, nil
		}
		if vs.Location() == location {
			return vs, nil
		}
		return vs.In(location), nil
	case int:
		if vs >= 0 {
			return time.Unix(int64(vs), 0).In(location), nil
		}
	case int64:
		if vs >= 0 {
			return time.Unix(vs, 0).In(location), nil
		}
	case int32:
		if vs >= 0 {
			return time.Unix(int64(vs), 0).In(location), nil
		}
	case int16:
		if vs >= 0 {
			return time.Unix(int64(vs), 0).In(location), nil
		}
	case int8:
		if vs >= 0 {
			return time.Unix(int64(vs), 0).In(location), nil
		}
	case uint:
		return time.Unix(int64(vs), 0).In(location), nil
	case uint64:
		return time.Unix(int64(vs), 0).In(location), nil
	case uint32:
		return time.Unix(int64(vs), 0).In(location), nil
	case uint16:
		return time.Unix(int64(vs), 0).In(location), nil
	case uint8:
		return time.Unix(int64(vs), 0).In(location), nil
	case string:
		if to, err := cast.StringToDateInDefaultLocation(vs, location); err == nil {
			if to.Location() == location {
				return to, nil
			}
			return to.In(location), nil
		}
	case json.Number:
		vss := string(vs)
		//纯数字处理
		if IsUint(vss) {
			to, err := strconv.ParseInt(vss, 0, 0)
			if err == nil {
				return time.Unix(to, 0).In(location), nil
			}
		}
	}
	return time.Time{}, fmt.Errorf("ToTimeWith: unable to %#v of type %T to time.Time", v, v)
}

// ToTimeDuration 转换目标为time.Duration, 转换失败则返回零值的Duration
func ToTimeDuration(v any) time.Duration {
	if v == nil {
		return 0
	}
	to, _ := cast.ToDurationE(v)
	return to
}

// ToTimeDurationD 转换目标为time.Duration, 转换失败则返回defaultVal指定的Duration对象
func ToTimeDurationD(v any, defaultVal time.Duration) time.Duration {
	if v == nil {
		return defaultVal
	}
	if to, err := cast.ToDurationE(v); err != nil {
		return defaultVal
	} else {
		return to
	}
}

// ToTimeDuration 转换目标为time.Duration, 转换失败则返回零值的Duration和错误信息
func ToTimeDurationE(v any) (time.Duration, error) {
	if v == nil {
		return 0, errors.New("ToTimeDurationE: nil pointer not support")
	}
	return cast.ToDurationE(v)
}

// ToTimeMonth 转换目标为time.Month, 转换失败则返回零值的Month
func ToTimeMonth(v any) (toVal time.Month) {
	if v == nil {
		return
	}
	to, _ := ToTimeMonthE(v)
	return to
}

// ToTimeMonthD 转换目标为time.Month, 转换失败则返回defaultVal指定的Month对象
func ToTimeMonthD(v any, defaultVal time.Month) time.Month {
	if v == nil {
		return defaultVal
	}
	if to, err := ToTimeMonthE(v); err != nil {
		return defaultVal
	} else {
		return to
	}
}

// ToTimeMonthE 转换目标为time.Month, 转换失败则返回零值的Month和错误信息
func ToTimeMonthE(v any) (toVal time.Month, err error) {
	if v == nil {
		return toVal, errors.New("ToTimeMonthE: nil pointer not support")
	}
	v, err = _anyToBaseParseV[any](v)
	if err != nil {
		return toVal, err
	}
	switch vs := v.(type) {
	case time.Time:
		if vs.IsZero() {
			return toVal, errors.New("ToTimeMonthE: zero time.Time")
		}
		return vs.Month(), nil
	case time.Month:
		return vs, nil
	case int:
		return toTimeMonthWithNumber(vs)
	case int8:
		return toTimeMonthWithNumber(vs)
	case int16:
		return toTimeMonthWithNumber(vs)
	case int32:
		return toTimeMonthWithNumber(vs)
	case int64:
		return toTimeMonthWithNumber(vs)
	case uint:
		return toTimeMonthWithNumber(vs)
	case uint8:
		return toTimeMonthWithNumber(vs)
	case uint16:
		return toTimeMonthWithNumber(vs)
	case uint32:
		return toTimeMonthWithNumber(vs)
	case uint64:
		return toTimeMonthWithNumber(vs)
	case json.Number:
		vss := string(vs)
		//纯数字处理
		if IsUint(vss) {
			to, err := strconv.ParseInt(vss, 0, 0)
			if err == nil {
				return toTimeMonthWithNumber(to)
			}
		}
	case string:
		switch strings.ToLower(vs) {
		case "january", "jan", "1", "1月", "一月":
			return time.January, nil
		case "february", "feb", "2", "2月", "二月":
			return time.February, nil
		case "march", "mar", "3", "3月", "三月":
			return time.March, nil
		case "april", "apr", "41", "4月", "四月":
			return time.April, nil
		case "may", "5", "5月", "五月":
			return time.May, nil
		case "june", "jun", "6", "6月", "六月":
			return time.June, nil
		case "july", "jul", "7", "7月", "七月":
			return time.July, nil
		case "august", "aug", "8", "8月", "八月":
			return time.August, nil
		case "september", "sep", "9", "9月", "九月":
			return time.September, nil
		case "october", "oct", "10", "10月", "十月":
			return time.October, nil
		case "november", "nov", "11", "11月", "十一月":
			return time.November, nil
		case "december", "dec", "12", "12月", "十二月":
			return time.December, nil
		}

	}
	return toVal, fmt.Errorf("ToTimeMonthE: unable to %#v of type %T to time.Month", v, v)
}
func toTimeMonthWithNumber[T TypeInt | TypeUint](v T) (time.Month, error) {
	if v >= 1 && v <= 12 {
		return time.Month(int(v)), nil
	} else {
		return GetZero[time.Month](), fmt.Errorf("ToTimeMonthE: unable to %#v of type %T to time.Month", v, v)
	}
}

// ToTimeWeekday 转换目标为time.Weekday, 转换失败则返回零值的Weekday
func ToTimeWeekday(v any) (toVal time.Weekday) {
	if v == nil {
		return
	}
	to, _ := ToTimeWeekdayE(v)
	return to
}

// ToTimeMonthD 转换目标为time.Weekday, 转换失败则返回defaultVal指定的Weekday对象
func ToTimeWeekdayD(v any, defaultVal time.Weekday) time.Weekday {
	if v == nil {
		return defaultVal
	}
	if to, err := ToTimeWeekdayE(v); err != nil {
		return defaultVal
	} else {
		return to
	}
}

// ToTimeWeekdayE 转换目标为time.Weekday, 转换失败则返回零值的Weekday和错误信息
func ToTimeWeekdayE(v any) (toVal time.Weekday, err error) {
	if v == nil {
		return toVal, errors.New("ToTimeWeekdayE: nil pointer not support")
	}
	v, err = _anyToBaseParseV[any](v)
	if err != nil {
		return toVal, err
	}
	switch vs := v.(type) {
	case time.Time:
		if vs.IsZero() {
			return toVal, errors.New("ToTimeWeekdayE: zero time.Time")
		}
		return vs.Weekday(), nil
	case time.Weekday:
		return vs, nil
	case int:
		return toTimeWeekdayWithNumber(vs)
	case int8:
		return toTimeWeekdayWithNumber(vs)
	case int16:
		return toTimeWeekdayWithNumber(vs)
	case int32:
		return toTimeWeekdayWithNumber(vs)
	case int64:
		return toTimeWeekdayWithNumber(vs)
	case uint:
		return toTimeWeekdayWithNumber(vs)
	case uint8:
		return toTimeWeekdayWithNumber(vs)
	case uint16:
		return toTimeWeekdayWithNumber(vs)
	case uint32:
		return toTimeWeekdayWithNumber(vs)
	case uint64:
		return toTimeWeekdayWithNumber(vs)
	case json.Number:
		vss := string(vs)
		//纯数字处理
		if IsUint(vss) {
			to, err := strconv.ParseInt(vss, 0, 0)
			if err == nil {
				return toTimeWeekdayWithNumber(to)
			}
		}
	case string:
		switch strings.ToLower(vs) {
		case "sunday", "sun", "0", "周7", "周日", "星期日", "星期天":
			return time.Sunday, nil
		case "monday", "mon", "1", "周1", "周一", "星期一":
			return time.Monday, nil
		case "tuesday", "tue", "2", "周2", "周二", "星期二":
			return time.Tuesday, nil
		case "wednesday", "wed", "3", "周3", "周三", "星期三":
			return time.Wednesday, nil
		case "thursday", "thu", "4", "周4", "周四", "星期四":
			return time.Thursday, nil
		case "friday", "fri", "5", "周5", "周五", "星期五":
			return time.Friday, nil
		case "saturday", "sat", "6", "周6", "周六", "星期六":
			return time.Saturday, nil
		}
	}
	return toVal, fmt.Errorf("ToTimeWeekdayE: unable to %#v of type %T to time.Month", v, v)
}
func toTimeWeekdayWithNumber[T TypeInt | TypeUint](v T) (toVal time.Weekday, err error) {
	if v >= 0 && v <= 6 {
		return time.Weekday(int(v)), nil
	} else {
		return toVal, fmt.Errorf("ToTimeWeekdayE: unable to %#v of type %T to time.Weekday", v, v)
	}
}

// ToUnix 转换为Unix时间戳(秒), 如果转换失败, 则返回0值
//   - 如待转换参数值为日期时间字符串，则使用UTC时区
func ToUnix(s any) int64 {
	if s == nil {
		return 0
	}
	if to, err := ToUnixWith(s, time.UTC); err == nil {
		return to
	}
	return 0
}

// ToUnixD 转换为Unix时间戳(秒), 如果转换失败, 则返回defaultVal指定的默认值
//   - 如待转换参数值为日期时间字符串，则使用UTC时区
func ToUnixD(s any, defaultVal int64) int64 {
	if s == nil {
		return defaultVal
	}
	if to, err := ToUnixWith(s, time.UTC); err == nil {
		return to
	}
	return defaultVal
}

// ToUnixWith 转换any目标为Unix时间戳(秒), 转换失败则返回0值和错误信息
//   - location 如待转换目标是日期时间字符串未包含时区信息时则使用location指定的时区
func ToUnixWith(s any, location *time.Location) (int64, error) {
	if s == nil {
		return 0, errors.New("ToUnixWith: nil pointer not support")
	}
	if location == nil {
		return 0, errors.New("ToUnixWith: location is nil")
	}
	switch vs := s.(type) {
	case time.Time:
		if vs.IsZero() {
			return 0, nil
		}
		if vs.Location() == location {
			return vs.Unix(), nil
		}
		return vs.In(location).Unix(), nil
		// 纯数字
	case int:
		if vs >= 0 {
			return int64(vs), nil
		}
	case int64:
		if vs >= 0 {
			return vs, nil
		}
	case int32:
		if vs >= 0 {
			return int64(vs), nil
		}
	case int16:
		if vs >= 0 {
			return int64(vs), nil
		}
	case int8:
		if vs >= 0 {
			return int64(vs), nil
		}
	case uint:
		return int64(vs), nil
	case uint64:
		return int64(vs), nil
	case uint32:
		return int64(vs), nil
	case uint16:
		return int64(vs), nil
	case uint8:
		return int64(vs), nil
	case float64:
		if vs >= 0 {
			return int64(vs), nil
		}
	case float32:
		if vs >= 0 {
			return int64(vs), nil
		}
	case json.Number:
		vss := string(vs)
		//纯数字处理
		if IsUint(vss) {
			v, err := strconv.ParseInt(vss, 0, 0)
			if err == nil {
				return v, nil
			}
			return 0, fmt.Errorf("ToUnixWith: type[json.Number] %#v not support", s)
		}
	//字符串
	case string:
		//纯数字处理
		if IsUint(vs) {
			v, err := strconv.ParseInt(vs, 0, 0)
			if err == nil {
				return v, nil
			}
			return 0, fmt.Errorf("ToUnixWith: type[string] %s not support", s)
		}
		//日期时间字符串
		if v, e := cast.StringToDateInDefaultLocation(vs, location); e == nil {
			return v.Unix(), nil
		}
	}
	return 0, fmt.Errorf("ToUnixWith: type[%T] %#v not support", s, s)
}

// ToUnixMilli 转换为Unix时间戳(毫秒), 如果转换失败, 则返回0值
//   - 如待转换参数值为日期时间字符串，则使用UTC时区
func ToUnixMilli(s any) int64 {
	if s == nil {
		return 0
	}
	if to, err := ToUnixMilliWith(s, time.UTC); err == nil {
		return to
	}
	return 0
}

// ToUnixMilliD 转换为Unix时间戳(毫秒), 如果转换失败, 则返回defaultVal指定的默认值
//   - 如待转换参数值为日期时间字符串，则使用UTC时区
func ToUnixMilliD(s any, defaultVal int64) int64 {
	if s == nil {
		return defaultVal
	}
	if to, err := ToUnixMilliWith(s, time.UTC); err == nil {
		return to
	}
	return defaultVal
}

// ToUnixMilliWith 转换any目标为Unix时间戳(毫秒), 转换失败则返回0值和错误信息
//   - location 如待转换目标是日期时间字符串未包含时区信息时则使用location指定的时区
func ToUnixMilliWith(s any, location *time.Location) (int64, error) {
	if s == nil {
		return 0, errors.New("ToUnixMilliWith: nil pointer not support")
	}
	if location == nil {
		return 0, errors.New("ToUnixMilliWith: location is nil")
	}
	switch vs := s.(type) {
	case time.Time:
		if vs.IsZero() {
			return 0, nil
		}
		if vs.Location() == location {
			return vs.UnixMilli(), nil
		}
		return vs.In(location).UnixMilli(), nil
		// 纯数字
	case int:
		if vs >= 0 {
			return int64(vs), nil
		}
	case int64:
		if vs >= 0 {
			return vs, nil
		}
	case int32:
		if vs >= 0 {
			return int64(vs), nil
		}
	case int16:
		if vs >= 0 {
			return int64(vs), nil
		}
	case int8:
		if vs >= 0 {
			return int64(vs), nil
		}
	case uint:
		return int64(vs), nil
	case uint64:
		return int64(vs), nil
	case uint32:
		return int64(vs), nil
	case uint16:
		return int64(vs), nil
	case uint8:
		return int64(vs), nil
	case float64:
		if vs >= 0 {
			return int64(vs), nil
		}
	case float32:
		if vs >= 0 {
			return int64(vs), nil
		}
	case json.Number:
		vss := string(vs)
		//纯数字处理
		if IsUint(vss) {
			v, err := strconv.ParseInt(vss, 0, 0)
			if err == nil {
				return v, nil
			}
			return 0, fmt.Errorf("ToUnixMilliWith: type[json.Number] %#v not support", s)
		}
	//字符串
	case string:
		//纯数字处理
		if IsUint(vs) {
			v, err := strconv.ParseInt(vs, 0, 0)
			if err == nil {
				return v, nil
			}
			return 0, fmt.Errorf("ToUnixMilliWith: type[string] %s not support", s)
		}
		//日期时间字符串
		if v, e := cast.StringToDateInDefaultLocation(vs, location); e == nil {
			return v.UnixMilli(), nil
		}
	}
	return 0, fmt.Errorf("ToUnixMilliWith: type[%T] %#v not support", s, s)
}
